package modelstore

import (
	"encoding/base64"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"sugar/utils"
)

type GitlabRequestError struct {
}

func (c GitlabRequestError) Error() string {
	return "Gitlab Api Response Error"
}

type ModelStoreGitlab struct {
	token string
	url   string
}

//
func NewModelStoreGitlabOp() *ModelStoreGitlab {
	return &ModelStoreGitlab{token: utils.GetEnvDefault("GITLAB_TOKEN", "KhQsgM8KFdVkRLLj2Brr"), url: utils.GetEnvDefault("GITLAB_URL", "http://221.224.36.165:10231")}
}

func (c *ModelStoreGitlab) createProject(name string) (string, error) {
	project_req_map := make(map[string]interface{})
	project_req_map["name"] = name
	project_req_json, err := json.Marshal(project_req_map)
	if err != nil {
		return "", err
	}
	req, err := http.NewRequest(http.MethodPost, c.url+"/api/v4/projects", strings.NewReader(string(project_req_json)))
	if err != nil {
		return "", err
	}
	req.Header.Add("PRIVATE-TOKEN", c.token)
	req.Header.Add("Content-type", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", err
	}
	if res.StatusCode != 201 {
		return "", GitlabRequestError{}
	}
	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", err
	}
	res_json := make(map[string]interface{})
	err = json.Unmarshal(body, &res_json)
	if err != nil {
		return "", err
	}
	project_id := int(res_json["id"].(float64))
	sproject_id := strconv.Itoa(project_id)
	return sproject_id, nil
}

func (c *ModelStoreGitlab) getProjectId(name string) (string, error) {
	req, err := http.NewRequest(http.MethodGet, c.url+"/api/v4/projects", nil)
	if err != nil {
		return "", err
	}
	req.Header.Add("PRIVATE-TOKEN", c.token)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", err
	}
	if res.StatusCode != 200 {
		return "", GitlabRequestError{}
	}
	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", err
	}
	res_json := make([]interface{}, 100)
	err = json.Unmarshal(body, &res_json)
	if err != nil {
		return "", err
	}
	var project_id int
	for _, value := range res_json {
		data := value.(map[string]interface{})
		if data["name"].(string) == name {
			fid, ok := data["id"].(float64)
			if !ok {
				continue
			}
			project_id = int(fid)
			sproject_id := strconv.Itoa(project_id)
			return sproject_id, nil
		}
	}
	return "", nil
}

//
func (c *ModelStoreGitlab) Create(modelID string, commitMessage string, user string, bytes []byte, config []byte) (string, error) {
	sprojectID, err := c.createProject(modelID)
	if err != nil {
		return "", err
	}
	initjson := make(map[string]interface{})
	initjson["branch"] = "master"
	initjson["author_email"] = user
	initjson["author_name"] = user
	initjson["commit_message"] = commitMessage
	actions := make([]map[string]interface{}, 0)
	action := make(map[string]interface{})
	action["content"] = base64.StdEncoding.EncodeToString(bytes)
	action["encoding"] = "base64"
	action["file_path"] = "model.py"
	action["action"] = "create"
	actions = append(actions, action)

	actionConfig := make(map[string]interface{})
	actionConfig["content"] = base64.StdEncoding.EncodeToString(config)
	actionConfig["encoding"] = "base64"
	actionConfig["file_path"] = "config.json"
	actionConfig["action"] = "create"
	actions = append(actions, actionConfig)

	initjson["actions"] = actions
	body, err := json.Marshal(initjson)
	if err != nil {
		return "", err
	}

	req, err := http.NewRequest(http.MethodPost, c.url+"/api/v4/projects/"+sprojectID+"/repository/commits", strings.NewReader(string(body)))
	if err != nil {
		return "", err
	}
	req.Header.Add("PRIVATE-TOKEN", c.token)
	req.Header.Add("Content-type", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", err
	}
	if res.StatusCode != 201 {
		return "", GitlabRequestError{}
	}

	defer res.Body.Close()
	bodyRes, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", err
	}
	resJson := make(map[string]interface{})
	err = json.Unmarshal(bodyRes, &resJson)
	if err != nil {
		return "", err
	}
	commitID := resJson["id"].(string)
	return commitID, nil
}

//
func (c *ModelStoreGitlab) Save(modelID string, branch string, commitMessage string, user string, bytes []byte, config []byte) (string, error) {
	sproject_id, err := c.getProjectId(modelID)
	if err != nil {
		return "", err
	}
	initjson := make(map[string]interface{})
	initjson["branch"] = branch
	initjson["author_email"] = user
	initjson["author_name"] = user
	initjson["commit_message"] = commitMessage
	actions := make([]map[string]interface{}, 0)
	action := make(map[string]interface{})
	action["content"] = base64.StdEncoding.EncodeToString(bytes)
	action["encoding"] = "base64"
	action["file_path"] = "model.py"
	action["action"] = "update"
	actions = append(actions, action)

	actionConfig := make(map[string]interface{})
	actionConfig["content"] = base64.StdEncoding.EncodeToString(config)
	actionConfig["encoding"] = "base64"
	actionConfig["file_path"] = "config.json"
	actionConfig["action"] = "update"
	actions = append(actions, actionConfig)

	initjson["actions"] = actions
	body, err := json.Marshal(initjson)
	if err != nil {
		return "", err
	}

	req, err := http.NewRequest(http.MethodPost, c.url+"/api/v4/projects/"+sproject_id+"/repository/commits", strings.NewReader(string(body)))
	if err != nil {
		return "", err
	}
	req.Header.Add("PRIVATE-TOKEN", c.token)
	req.Header.Add("Content-type", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", err
	}
	if res.StatusCode != 201 {
		return "", GitlabRequestError{}
	}

	defer res.Body.Close()
	bodyRes, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", err
	}
	resJson := make(map[string]interface{})
	err = json.Unmarshal(bodyRes, &resJson)
	if err != nil {
		return "", err
	}
	commitID := resJson["id"].(string)
	return commitID, nil
}

//
func (c *ModelStoreGitlab) Load(modelID string, ref string) ([]byte, map[string]interface{}, error) {
	sprojectID, err := c.getProjectId(modelID)
	if err != nil {
		return nil, nil, err
	}
	req, err := http.NewRequest("GET", c.url+"/api/v4/projects/"+sprojectID+"/repository/files/model.py?ref="+ref, nil)
	if err != nil {
		return nil, nil, err
	}
	req.Header.Add("PRIVATE-TOKEN", c.token)
	req.Header.Add("Content-type", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, nil, err
	}
	if res.StatusCode != 200 {
		return nil, nil, err
	}
	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, nil, err
	}
	resJson := make(map[string]interface{})
	err = json.Unmarshal(body, &resJson)
	if err != nil {
		return nil, nil, err
	}
	content := resJson["content"].(string)
	bytes, err := base64.StdEncoding.DecodeString(content)

	reqConfig, err := http.NewRequest("GET", c.url+"/api/v4/projects/"+sprojectID+"/repository/files/config.json?ref="+ref, nil)
	if err != nil {
		return nil, nil, err
	}
	reqConfig.Header.Add("PRIVATE-TOKEN", c.token)
	reqConfig.Header.Add("Content-type", "application/json")
	resConfig, err := http.DefaultClient.Do(reqConfig)
	if err != nil {
		return nil, nil, err
	}
	if resConfig.StatusCode != 200 {
		return nil, nil, err
	}
	defer resConfig.Body.Close()
	bodyConfig, err := ioutil.ReadAll(resConfig.Body)
	if err != nil {
		return nil, nil, err
	}
	configMap := make(map[string]interface{})
	err = json.Unmarshal(bodyConfig, &configMap)
	if err != nil {
		return nil, nil, err
	}
	contentConfig := configMap["content"].(string)
	configBytes, err := base64.StdEncoding.DecodeString(contentConfig)
	err = json.Unmarshal(configBytes, &configMap)
	if err != nil {
		return nil, nil, err
	}
	return bytes, configMap, err
}

//
func (c *ModelStoreGitlab) History(modelID string) ([]byte, error) {
	sprojectID, err := c.getProjectId(modelID)
	if err != nil {
		return nil, err
	}
	req, err := http.NewRequest("GET", c.url+"/api/v4/projects/"+sprojectID+"/repository/commits?ref_name=master", nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("PRIVATE-TOKEN", c.token)
	req.Header.Add("Content-type", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	if res.StatusCode != 200 {
		return nil, err
	}
	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}
	return body, nil
}

//
func (c *ModelStoreGitlab) Branch(modelID string, ref string, newBranch string) error {
	sprojectID, err := c.getProjectId(modelID)
	if err != nil {
		return err
	}
	initjson := make(map[string]interface{})
	initjson["branch"] = newBranch
	initjson["ref"] = ref
	body, err := json.Marshal(initjson)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("POST", c.url+"/api/v4/projects/"+sprojectID+"/repository/branches", strings.NewReader(string(body)))
	if err != nil {
		return err
	}
	req.Header.Add("PRIVATE-TOKEN", c.token)
	req.Header.Add("Content-type", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	if res.StatusCode != 201 {
		return err
	}
	// defer res.Body.Close()
	// bodyRes, err := ioutil.ReadAll(res.Body)
	// if err != nil {
	// 	return "", err
	// }
	// resJson := make(map[string]interface{})
	// err = json.Unmarshal(bodyRes, &resJson)
	// if err != nil {
	// 	return "", err
	// }
	// commitID := resJson["id"].(string)
	return nil
}
